package com.cj.gateway.jwtutil;

import com.alibaba.nacos.api.utils.StringUtils;
import com.cj.common.response.Result;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.jsonwebtoken.Claims;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Date;

/**
 * @Author cheng jun
 * @Description:
 * @Date 2021/5/30 20:45
 */
/**
 * 支持Nacos的动态刷新功能
 */
@RefreshScope
@Component
@Slf4j
public class JwtTokenFilter implements GlobalFilter, Ordered {

    @Value("${org.my.jwt.skip-auth-urls}")
    private String skipAuthUrls;

    @Value("${org.my.jwt.jwt-header-name}")
    private String jwtHeaderName;

    @Value("${org.my.jwt.jwt-key}")
    private String jwtKey;

    @Value("${org.my.jwt.jwt-ttl}")
    private String jwtTtl;

    @Value("${org.my.jwt.jwt-header}")
    private String jwtHeader;

    private ObjectMapper objectMapper;

    public JwtTokenFilter(ObjectMapper objectMapper) {
        this.objectMapper = objectMapper;
    }

    /**
     * 过滤器
     * @param exchange
     * @param chain
     * @return
     */
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        log.info("网关服务:进入过滤器-------------------------------------------");

        String url = exchange.getRequest().getURI().getPath();

        // 跳过不需要验证的路径
        String[] skipAuthUrlsArray = skipAuthUrls.split(",");
        for (String s : skipAuthUrlsArray) {
            if(Arrays.asList(url.split("/")).contains(s)){
                return chain.filter(exchange);
            }
        }

        // 获取token
        String token = exchange.getRequest().getHeaders().getFirst("token");
        ServerHttpResponse resp = exchange.getResponse();
        if(StringUtils.isBlank(token)){
            // 没有token
            return authErro(resp, Result.error(Result.CodeMsg.PLEASE_LOG_IN));
        }else{
            // 有token
            try {
                // 不管是否过期，都返回claims对象
                Claims claims = new JWTUtil(jwtKey, jwtTtl).parseJWT(token);
                Date expiration = claims.getExpiration();
                // 和当前时间进行对比来判断是否过期
                if (new Date(System.currentTimeMillis()).after(expiration)) {
                    return authErro(resp, Result.error(Result.CodeMsg.TOKEN_EXPIRED));
                }
                String userStr = claims.getSubject();
                log.info("AuthGlobalFilter.filter() user:{}", userStr);
                // 从token中解析用户信息并设置到Header中去
                // ServerHttpRequest request = exchange.getRequest().mutate().header("user", userStr).build();
                ServerHttpRequest request = exchange.getRequest().mutate()
                        .header("user", userStr)
                        .header(jwtHeaderName, jwtHeader).build();
                exchange = exchange.mutate().request(request).build();
                return chain.filter(exchange);
            }catch (Exception e) {
                log.error(e.getMessage());
                return authErro(resp, Result.error(Result.CodeMsg.AUTHENTICATION_FAILED));
            }
        }
    }

    /**
     * 认证错误输出
     * @param resp 响应对象
     * @param mess 错误信息
     * @return
     */
    private Mono<Void> authErro(ServerHttpResponse resp, Object mess) {
        resp.setStatusCode(HttpStatus.OK);
        resp.getHeaders().add("Content-Type","application/json;charset=UTF-8");
        // ReturnData<String> returnData = new ReturnData<>(org.apache.http.HttpStatus.SC_UNAUTHORIZED, mess, mess);
        String returnStr = "";
        try {
            returnStr = objectMapper.writeValueAsString(mess);
        } catch (JsonProcessingException e) {
            log.error(e.getMessage(),e);
        }
        DataBuffer buffer = resp.bufferFactory().wrap(returnStr.getBytes(StandardCharsets.UTF_8));
        return resp.writeWith(Flux.just(buffer));
    }

    @Override
    public int getOrder() {
        return -100;
    }
}